// Copyright (c) Microsoft Corporation.  All rights reserved.
#include "Common.h"

//
// Convert UTF-8 String to UTF-16 String
// Caller must delete [] *ppszWideChar
//
HRESULT UTF8ToWideChar(
    __in_bcount(cbUTF8) const BYTE *pUTF8,
    __in DWORD cbUTF8,
    __deref_out_ecount(*pcchWideChar) LPWSTR *ppszWideChar,
    __out DWORD *pcchWideChar)
{
    HRESULT hr = S_OK;
    wchar_t* pwsz = NULL;
    
    if (!ppszWideChar || !pcchWideChar)
    {
        return E_POINTER;
    }

    *ppszWideChar = NULL;
    *pcchWideChar = 0;
    
    int cch = MultiByteToWideChar(CP_UTF8, // Code page
                                  0, // Character-type options
                                  (LPCSTR)pUTF8, // String to map
                                  cbUTF8, // Number of bytes in string
                                  NULL,
                                  0); // Only return the buffer size (in wide characters) required

    if (cch > 0)
    {
        // special handling in case the string is not null terminated.
        if (pUTF8[cbUTF8 - 1] != '\0')
        {
            cch++;    // making a space for null
        }

        pwsz = new(std::nothrow) wchar_t[cch];

        if (pwsz != NULL)
        {
            if (MultiByteToWideChar(CP_UTF8, // Code page
                                         0, // Character-type options
                                         (LPCSTR)pUTF8, // String to map
                                         cbUTF8, // Number of bytes in string
                                         pwsz, // Wide-character buffer
                                         cch) != 0) // Size of buffer
            {
                pwsz[cch - 1] = L'\0';
            }
            else
            {
                hr = E_FAIL;
            }
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (hr == S_OK)
    {
        *ppszWideChar = pwsz;
        *pcchWideChar = cch;
    }
    else
    {
        if (pwsz)
        {
            delete [] pwsz;
        }
    }

    return hr;
}

//
// Get SID Name
// Caller should do delete [] for returned names.
//
HRESULT GetAccountSidName(
    __in PSID pSid,
    __deref_out LPWSTR *ppwszUserName,
    __deref_out LPWSTR *ppwszDomainName)
{
    HRESULT hr = S_OK;
    WCHAR *pwszUserName = NULL;
    WCHAR *pwszDomainName = NULL;
    DWORD cchUserName = 0;
    DWORD cchDomainName = 0;
    SID_NAME_USE sidType;
    int retry = 3;      // try maximum 3 times.
                        // 1st time to get length of names
                        // 2nd time to get names.
                        // 3rd time to get names again if fails in 2nd.
    
    if (!pSid)
    {
        return E_INVALIDARG;
    }
    
    if (!ppwszUserName || !ppwszDomainName)
    {
        return E_POINTER;
    }

    //
    while(retry--)
    {
        if (LookupAccountSidW(NULL,
                    pSid,
                    pwszUserName,
                    &cchUserName,
                    pwszDomainName,
                    &cchDomainName,
                    &sidType))
        {
            // succeeded
            *ppwszUserName = pwszUserName;
            *ppwszDomainName = pwszDomainName;
            break;
        }
        else
        {
            int err = GetLastError();
            
            if (err == ERROR_INSUFFICIENT_BUFFER)
            {
                if (pwszUserName)   delete [] pwszUserName;
                if (pwszDomainName) delete [] pwszDomainName;
                
                pwszUserName   = new(std::nothrow) WCHAR[cchUserName   + 1];
                pwszDomainName = new(std::nothrow) WCHAR[cchDomainName + 1];
                
                if (!pwszUserName || !pwszDomainName)
                {
                    hr = E_OUTOFMEMORY;
                    break;
                }
            }
            else
            {
                hr = HRESULT_FROM_WIN32(err);
                break;
            }
        }
    }
    
    if (FAILED(hr))
    {
        if (pwszUserName)   delete [] pwszUserName;
        if (pwszDomainName) delete [] pwszDomainName;
    }
    
    return hr;
}


